/*
 * Copyright 2007-2010 Jeremy Brown.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @author <a href="http://jeremy-brown.com">Jeremy Brown</a>
 */


package groovy.prefuse.demos

import groovy.swing.*
import groovy.prefuse.*
import groovy.prefuse.factory.*
import groovy.prefuse.impl.layout.*
import groovy.prefuse.impl.controls.*

import java.awt.*
import java.awt.event.*
import java.awt.geom.*
import javax.swing.*

import prefuse.*
import prefuse.action.*
import prefuse.action.assignment.*
import prefuse.action.layout.*
import prefuse.action.layout.graph.*
import prefuse.activity.*
import prefuse.controls.*
import prefuse.data.*
import prefuse.render.*
import prefuse.util.*
import prefuse.util.ui.*
import prefuse.visual.*


/**
 * Demo application showcasing the use of AggregateItems to
 * visualize groupings of nodes with in a graph visualization.
 * 
 * This class uses the AggregateLayout class to compute bounding
 * polygons for each aggregate and the AggregateDragControl to
 * enable drags of both nodes and node aggregates.
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class AggregateDemo
{

    public static final String GRAPH = "graph"
    public static final String NODES = "graph.nodes"
    public static final String EDGES = "graph.edges"
    public static final String AGGR = "aggregates"
    public static final String AGGDECO = "aggDeco"
    
    static PrefuseBuilder swing
    
    public static void main(String[] args)
    {
		UILib.setPlatformLookAndFeel();

		swing = new PrefuseBuilder()
		swing.registerPrefuse()
		
		demo()
    }
    
    public static void demo() 
    {
		swing.build()
		{
			frame(id:"frame", title:"p r e f u s e  |  a g g r e g a t e d", defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE)
			{
				pfDisplay(id:"display", size:[500,500])
				{
					pfVisualization(id:"vis", interactive:[[EDGES, null, false], [AGGDECO, null, false]])
					{
						pfGraph(id:"graph", columnNames:["name", "type"], columnTypes:[String, String], visualID:"vg", group:GRAPH)
						{
							pfNode(["carp", "fish"], id:"n0"); pfNode(["cod", "fish"], id:"n1"); pfNode(["pufferfish", "fish"], id:"n2");
							pfEdge(n0, n1, n2, n0)
							pfNode(["dog", "mammal"], id:"n3"); pfNode(["sloth", "mammal"], id:"n4"); pfNode(["porcupine", "mammal"], id:"n5"); pfNode(["dolphin", "mammal"], id:"n10"); pfNode(["human", "mammal"], id:"n11");
							pfEdge(n3, n4, n5, n10, n11, n3)
							pfNode(["eagle", "bird"], id:"n6"); pfNode(["ostrich", "bird"], id:"n7"); pfNode(["vulture", "bird"], id:"n8"); pfNode(["penguin", "bird"], id:"n9");
							pfEdge(n6, n7, n8, n9, n6)

							pfEdge(n0, n3, n6, n0)
						}
						
						pfPolygonRenderer(id:"polyR", predicate:"ingroup('aggregates')", polyType:Constants.POLY_TYPE_CURVE, curveSlack:0.15f)
						pfLabelRenderer(id:"labelR", textField:"name")
						
						initDataGroups()
						
						pfDecorator(group:AGGDECO, source:AGGR, defaults:[TEXTCOLOR:ColorLib.gray(128), FONT:FontLib.getFont("Tahoma", Font.BOLD, 20)])
						pfLabelRenderer(id:"labelR", predicate:"ingroup('aggDeco')", textField:"aggregateType")

						// now create the main layout routine
						pfActionList(id:"layout", name:"layout", duration:Activity.INFINITY)
						{
							//bundle the color actions
							pfActionList(id:"color", name:"color")
							{
								pfColorAction(group:NODES, id:"nText", field:VisualItem.TEXTCOLOR, defaultColor:[0,0,0])
								
								pfColorAction(id:"nStroke", group:NODES, field:VisualItem.STROKECOLOR, defaultColor:[100,100,100])
								{
									pfColorAction(predicate:"_hover", field:VisualItem.STROKECOLOR, defaultColor:[50,50,50])
								}
		
								pfColorAction(id:"nFill", group:NODES, field:VisualItem.FILLCOLOR, defaultColor:[255,255,255])
								{
									pfColorAction(predicate:"_hover", field:VisualItem.FILLCOLOR, defaultColor:[200,200,200])
								}
		
								pfColorAction(id:"nEdges", group:EDGES, field:VisualItem.STROKECOLOR, defaultColor:[100,100,100])
								pfColorAction(id:"aStroke", group:AGGR, field:VisualItem.STROKECOLOR, defaultColor:[200,200,200])
								{
									pfColorAction(predicate:"_hover", field:VisualItem.STROKECOLOR, defaultColor:[255,100,100])
								}
		
								pfDataColorAction(id:"aFill", group:AGGR, dataField:"aggregateType", dataType:Constants.NOMINAL, field:VisualItem.FILLCOLOR, palette:[[255,200,200,150],[200,255,200,150],[200,200,255,150]])
								pfColorAction(group:AGGR, id:"aText", field:VisualItem.TEXTCOLOR, defaultColor:[0,0,0])
							}
							pfForceDirectedLayout(group:GRAPH, enforceBounds:true)
							pfAggregateLayout(group:AGGR)
							pfLayout(new DecoratorLabelLayout(AGGDECO))
							pfRepaintAction()
						}
					}
					
					pfAggregateDragControl()
					pfZoomControl()
					pfPanControl()
				}
			}
		}

		swing.frame.pack()
		swing.frame.show()
		
        swing.display.pan(250, 250);
        swing.vis.run("layout")
    }
    

    private static void initDataGroups()
    {
		AggregateTable at = swing.vis.addAggregates(AGGR);
		at.addColumn(VisualItem.POLYGON, float[].class);

		at.addColumn("aggregateType", String);
		Map aggregates = [:]
		swing.vg.nodes().each{node->
			String type = node.getString("type")
			if(!aggregates.containsKey(type))
			{
				AggregateItem aggregate = (AggregateItem) at.addItem()
				aggregate.setString("aggregateType", type)
				aggregates.put(type, aggregate)
			}
			AggregateItem aggregate = (AggregateItem)aggregates.get(type)
			aggregate.addItem(node)
		}    
    }

    
} // end of class AggregateDemo
